package com.yilin.platform.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.gson.reflect.TypeToken;
import com.yilin.platform.base_core.application.AppUtil;
import com.yilin.platform.base_core.application.CapitalBaseEntity;
import com.yilin.platform.base_core.application.PageData;
import com.yilin.platform.base_core.commons.exception.BusinessException;
import com.yilin.platform.base_core.commons.utils.*;
import com.yilin.platform.base_core.components.FptClient;
import com.yilin.platform.base_core.components.StartupTimeListener;
import com.yilin.platform.base_core.tools.pab.base.BaseResult;
import com.yilin.platform.base_core.tools.pab.base.PabTrade;
import com.yilin.platform.base_core.tools.pab.base.ResResult;
import com.yilin.platform.base_core.tools.pab.entity.DataXmlHeader;
import com.yilin.platform.base_core.tools.pab.entity.req.*;
import com.yilin.platform.base_core.tools.pab.entity.res.*;
import com.yilin.platform.base_core.tools.pab.utils.PabTaskTools;
import com.yilin.platform.base_core.tools.pab.utils.PabXmlTools;
import com.yilin.platform.entity.capital.TradeFlow;
import com.yilin.platform.entity.pab.PabDockingLog;
import com.yilin.platform.entity.pab.PabFileDownLoad;
import com.yilin.platform.entity.pab.PabTradeRecord;
import com.yilin.platform.mapper.pab.PabDockingLogMapper;
import com.yilin.platform.mapper.pab.PabFileDownloadMapper;
import com.yilin.platform.mapper.pab.PabTradeRecordMapper;
import com.yilin.platform.service.IBankPabService;
import com.yilin.platform.service.ITradeFlowService;
import com.yilin.platform.service.IUserService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.util.*;

/***
 * 平安银行 · 银企直连
 * 客户调联接口
 * */
@Service
@Transactional
public class BankPabService extends ServiceImpl<PabTradeRecordMapper, PabTradeRecord> implements IBankPabService {
    private  static Logger logger = LoggerFactory.getLogger(BankPabService.class);
    @Resource
    PabXmlTools pabXmlTools;
    @Resource
    PabTaskTools pabTaskTools;
    @Resource
    FptClient fptClient;
    @Resource
    IUserService userService;
    @Resource
    ITradeFlowService tradeFlowService;
    @Resource
    StartupTimeListener startupTimeListener;

    @Resource
    PabTradeRecordMapper pabTradeRecordMapper;
    @Resource
    PabFileDownloadMapper pabFileDownloadMapper;
    @Resource
    PabDockingLogMapper pabDockingLogMapper;

    //--固定参数配置区
    @Value("${file-mapping-enterprise.save-path}")
    private String savePath;
    @Value("${file-mapping-enterprise.mapping-path}")
    private String mappingPath;
    @Value("${pab-bank-enterprise.separation}")
    private Boolean separation;
    @Value("${pab-bank-enterprise.download-path}")
    private String downloadPath;
    @Value("${pab-bank-enterprise.merchant-code}")
    private String merchantCode;
    @Value("${pab-bank-enterprise.req-url}")
    private String reqUrl;

    /**
     * 发起单笔4004
     * 核心交易
     */
    @Override
    public PabTradeRecord singleTrade(PabTradeRecord tradeRecord) {
        List<String> batchNoList = pabTaskTools.getBatchNoList();
        if (batchNoList.size() > 10) throw new BusinessException("警报！单笔待处理交易挤压严重，请稍后再试，若持续返回此警报，请通知开发人员处理");
        ReqBody4004 reqBody4004 = new ReqBody4004();
        reqBody4004.setThirdVoucher(tradeRecord.getTradeNo());//转账凭证号
        reqBody4004.setCcyCode("RMB");//默认人民币RMB账户
        reqBody4004.setOutAcctNo(tradeRecord.getOutAcctNo());//付款人账户
        reqBody4004.setOutAcctName(tradeRecord.getOutAcctName());//付款人名称
        reqBody4004.setInAcctNo(tradeRecord.getInAcctNo());//收款人账户 中国农业银行股份有限公司
        reqBody4004.setInAcctName(tradeRecord.getInAcctName());//收款人账户户名
        reqBody4004.setInAcctBankNode(tradeRecord.getInAcctBankNode());//收款人开户行行号
        reqBody4004.setInAcctRecCode(tradeRecord.getInAcctBankNode());//==接收行行号
        reqBody4004.setInAcctBankName(tradeRecord.getInAcctBankName());//收款人开户行名称
        reqBody4004.setInAcctProvinceCode(tradeRecord.getInAcctProvinceCode());//收款账户开户省代码
        reqBody4004.setInAcctCityName(tradeRecord.getInAcctCityName());//收款账户开户市
        reqBody4004.setUnionFlag(tradeRecord.getUnionFlag() ? "0" : "1");//1：行内转账，0：跨行转账
        reqBody4004.setTranAmount(CommonUtil.formatDoubleTwo(tradeRecord.getTranAmount()));//转出金额
        reqBody4004.setAddrFlag("1");//同城/异地标志
        //>>>>> 发送交易：获取交易结果
        ResResult<ReqBody4004, ResBody4004> result = postData(reqBody4004, ResBody4004.class);
        if (!result.getResHeader().getResCode().equals(PabXmlTools.RET_SUCCESS_CODE)) {
            throw new BusinessException(result.getResHeader().getResMsg());
        }
        putUpdatePaymentStatus(tradeRecord, result.getResBody().getStt());
        tradeRecord.setBankFlowNo(result.getResBody().getFrontLogNo());
        saveTradeFlow("基本转账交易", tradeRecord);
        //>>>> 使用Redis保存同步指令信息
        if (tradeRecord.getPabTradeStatus() == PabTradeRecord.PabTradeStatus.handle) {
            pabTaskTools.addTradeNoTask(tradeRecord.getTradeNo());
        }
        saveOrUpdate(tradeRecord);
        return tradeRecord;
    }

    /**
     * 发起多笔4018
     * 核心交易
     */
    @Override
    public String batchTrade(List<PabTradeRecord> tradeRecordList) {
        List<String> batchNoList = pabTaskTools.getBatchNoList();
        if (batchNoList.size() > 10) throw new BusinessException("警报！批量待处理交易挤压严重，请稍后再试，若持续返回此警报，请通知开发人员处理");
        String batchNo = DateTimeUtil.dateNo("BN");//统一批量的流水从这里保存
        int totalCount = tradeRecordList.size();
        for (PabTradeRecord pabTradeRecord : tradeRecordList) {
            pabTradeRecord.setTranAmount(CommonUtil.doubleTwo(pabTradeRecord.getTranAmount()));
        }
        double totalAmt = ArrayUtil.numListAdd(tradeRecordList, PabTradeRecord::getTranAmount);
        PabTradeRecord first = ArrayUtil.first(tradeRecordList);
        ReqBody4018 reqBody4018 = new ReqBody4018();
        reqBody4018.setThirdVoucher(batchNo);
        reqBody4018.setTotalCts(String.valueOf(totalCount));
        reqBody4018.setTotalAmt(CommonUtil.formatDoubleTwo(totalAmt));
        reqBody4018.setbSysFlag("N");//默认值
        reqBody4018.setCcyCode("RMB");//默认值-人民币
        reqBody4018.setOutAcctNo(first.getOutAcctNo());
        reqBody4018.setOutAcctName(first.getOutAcctName());
        List<ReqBody4018.HOResultSet4018R> set4018ItemList = new ArrayList<>();
        for (PabTradeRecord tradeRecord : tradeRecordList) {
            tradeRecord.setBatchNo(batchNo);
            tradeRecord.setPabTradeStatus(PabTradeRecord.PabTradeStatus.handle);
            ReqBody4018.HOResultSet4018R set4018Item = new ReqBody4018.HOResultSet4018R();
            set4018Item.setsThirdVoucher(tradeRecord.getTradeNo());//转账凭证号
            set4018Item.setInAcctNo(tradeRecord.getInAcctNo());//收款人账户
            set4018Item.setInAcctName(tradeRecord.getInAcctName());//收款人账户户名
            set4018Item.setUnionFlag(tradeRecord.getUnionFlag() ? "0" : "1");//1：行内转账，0：跨行转账
            set4018Item.setTranAmount(CommonUtil.formatDoubleTwo(tradeRecord.getTranAmount()));//转出金额
            set4018Item.setInAcctBankNode(tradeRecord.getInAcctBankNode());//收款人开户行行号
            set4018Item.setInAcctRecCode(tradeRecord.getInAcctBankNode());//==接收行行号
            set4018Item.setInAcctBankName(tradeRecord.getInAcctBankName());//收款人开户行名称
            set4018Item.setInAcctProvinceCode(tradeRecord.getInAcctProvinceCode());//收款账户开户省代码
            set4018Item.setInAcctCityName(tradeRecord.getInAcctCityName());//收款账户开户市
            set4018Item.setAddrFlag("1");//同城/异地标志
            set4018ItemList.add(set4018Item);
        }
        reqBody4018.setItemList(set4018ItemList);
        //>>>>> 发送交易：获取交易结果
        ResResult<ReqBody4018, ResBody4018> result = postData(reqBody4018, ResBody4018.class);
        if (!result.getResHeader().getResCode().equals(PabXmlTools.RET_SUCCESS_CODE)) {
            throw new BusinessException(result.getResHeader().getResMsg());
        }
        //>>>> 使用Redis保存同步指令信息
        pabTaskTools.addBatchNoTask(batchNo);
        saveOrUpdateBatch(tradeRecordList);
        return batchNo;
    }

    /**
     * 交易结果-处理指令查询
     */
    @Override
    public void dealSingleQueryTrade() {
        List<String> tradeNoList = pabTaskTools.getTradeNoList();
        if (tradeNoList.size() > 10) logger.info("警报！单笔待处理交易挤压严重，请通知财务人员减少交易频率");
        if (tradeNoList.isEmpty()){
            QueryWrapper<PabTradeRecord> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("pab_trade_status", PabTradeRecord.PabTradeStatus.handle.index);
            List<PabTradeRecord> pabTradeRecords = pabTradeRecordMapper.selectList(queryWrapper);
            if (!pabTradeRecords.isEmpty()){
                tradeNoList = ArrayUtil.fieldNoRepeatAdd(pabTradeRecords, CapitalBaseEntity::getTradeNo);
            }
        }
        for (String tradeNo : tradeNoList) {
            if (tradeNo == null) return;
            ResBody4005 resBody4005 = singleQueryTrade(tradeNo);
            // ### 查询时自动同步
            PabTradeRecord tradeRecord = getTradeRecordByNo(tradeNo);
            if (tradeRecord != null && tradeRecord.getPabTradeStatus() == PabTradeRecord.PabTradeStatus.handle) {
                if (resBody4005 == null) {
                    if (tradeRecord.getSyncTaskCount() < 3) return;
                    tradeRecord.setPabTradeStatus(PabTradeRecord.PabTradeStatus.fail);
                } else {
                    putUpdatePaymentStatus(tradeRecord, resBody4005.getStt());
                    tradeRecord.setBankFlowNo(resBody4005.getFrontLogNo());
                    tradeRecord.setCompleteTime(DateTimeUtil.stringToDate(resBody4005.getSubmitTime(),"yyyyMMddHHmmss"));
                }
                tradeRecord.setSyncTaskCount(tradeRecord.getSyncTaskCount() + 1);
                saveTradeFlow("基本转账交易", tradeRecord);
                if (tradeRecord.getPabTradeStatus() != PabTradeRecord.PabTradeStatus.handle) {
                    pabTaskTools.removeTradeNo(tradeNo);
                }
                updateById(tradeRecord);
                notifiedYilin(ArrayUtil.initList(tradeRecord));
            }
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                logger.info("处理PAB任务时线程延时异常");
            }
        }
    }

    /**
     * 交易结果-转账指令查询
     */
    @Override
    public ResBody4005 singleQueryTrade(String tradeNo) {
        ReqBody4005 reqBody4005 = new ReqBody4005();
        reqBody4005.setOrigThirdVoucher(tradeNo);
        //>>>>> 发送交易：获取交易结果
        ResResult<ReqBody4005, ResBody4005> result = postData(reqBody4005, ResBody4005.class);
        if (result.getResHeader().getResCode().equals("MA0103")) {
            return null;//交易待查行为：30分钟后重试
        } else if (!result.getResHeader().getResCode().equals(PabXmlTools.RET_SUCCESS_CODE)) {
            throw new BusinessException(result.getResHeader().getResMsg());
        }
        return result.getResBody();
    }

    /**
     * 交易结果-处理批量指令查询
     */
    @Override
    public void dealBatchQueryTrade() {
        List<String> batchNoList = pabTaskTools.getBatchNoList();
        if (batchNoList.size() > 10) logger.info("警报！批量待处理交易挤压严重，请通知财务人员减少交易频率");
        if (batchNoList.isEmpty()){
            QueryWrapper<PabTradeRecord> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("pab_trade_status", PabTradeRecord.PabTradeStatus.handle.index);
            List<PabTradeRecord> pabTradeRecords = pabTradeRecordMapper.selectList(queryWrapper);
            if (!pabTradeRecords.isEmpty()){
                batchNoList = ArrayUtil.fieldNoRepeatAdd(pabTradeRecords, PabTradeRecord::getBatchNo);
            }
        }
        for (String batchNo : batchNoList) {
            if (batchNo == null) return;
            ResBody4015 resBody4015 = batchQueryTrade(batchNo, ReqBody4015.queryTypeAll, 1, 500);
            // ### 批量必然是异步查
            List<ResBody4015.ResBody4015Item> itemList = resBody4015.getItemList();
            List<PabTradeRecord> tradeRecordList = getTradeRecordsByBatchNo(batchNo);
            for (ResBody4015.ResBody4015Item item : itemList) {
                PabTradeRecord tradeRecord = ArrayUtil.getBy(tradeRecordList, obj -> obj.getTradeNo().equals(item.getsThirdVoucher()));
                if (tradeRecord == null || tradeRecord.getPabTradeStatus() != PabTradeRecord.PabTradeStatus.handle) continue;
                putUpdatePaymentStatus(tradeRecord, item.getStt());
                tradeRecord.setBankFlowNo(item.getFrontLogNo());
                tradeRecord.setCompleteTime(DateTimeUtil.stringToDate(item.getSubmitTime(),"yyyyMMddHHmmss"));
                tradeRecord.setSyncTaskCount(tradeRecord.getSyncTaskCount() + 1);
                saveTradeFlow("基本转账交易", tradeRecord);
            }
            List<PabTradeRecord> pabTradeRecords = ArrayUtil.whereBy(tradeRecordList, obj -> obj.getPabTradeStatus() == PabTradeRecord.PabTradeStatus.handle);
            if (pabTradeRecords.size() == 0) pabTaskTools.removeBatchNo(batchNo);
            saveOrUpdateBatch(tradeRecordList);
            notifiedYilin(tradeRecordList);
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                logger.info("处理PAB任务时线程延时异常");
            }
        }
    }

    /**
     * 交易结果-批量指令查询
     */
    @Override
    public ResBody4015 batchQueryTrade(String batchNo, String queryType, Integer page, Integer limit) {
        if (queryType == null) queryType = ReqBody4015.queryTypeAll;
        if (page == null || page < 0) page = 1;
        if (limit == null || limit < 1) limit = 500;
        ReqBody4015 reqBody4015 = new ReqBody4015();
        reqBody4015.setOrigThirdVoucher(batchNo);
        reqBody4015.setQueryType(queryType);//
        reqBody4015.setPageCts(String.valueOf(limit));
        reqBody4015.setPageNo(String.valueOf(page));
        //>>>>> 发送交易：获取交易结果
        ResResult<ReqBody4015, ResBody4015> result = postData(reqBody4015, ResBody4015.class);
        if (result.getResHeader().getResCode().equals("MA0103")) {
            return null;//交易待查行为：30分钟后重试
        } else if (!result.getResHeader().getResCode().equals(PabXmlTools.RET_SUCCESS_CODE)) {
            throw new BusinessException(result.getResHeader().getResMsg());
        }
        return result.getResBody();
    }

    /**
     * 账户信息-余额查询
     */
    @Override
    public ResBody4001 queryAccount(String bankAccountNo) {
        ReqBody4001 reqBody4001 = new ReqBody4001();
        reqBody4001.setAccount(bankAccountNo);
        //>>>>> 发送交易：获取交易结果
        ResResult<ReqBody4001, ResBody4001> result = postData(reqBody4001, ResBody4001.class);
        if (!result.getResHeader().getResCode().equals(PabXmlTools.RET_SUCCESS_CODE)) {
            throw new BusinessException(result.getResHeader().getResMsg());
        }
        return result.getResBody();
    }

    /**
     * 明细报表查询接口
     */
    @Override
    public ResBody4013 queryTradeDetails(String bankAccountNo, Date startDate, Date endDate, Integer page, Integer limit) {
        if (page < 1) page = 1;
        if (limit > 100) page = 100;
        ReqBody4013 reqBody4013 = new ReqBody4013();
        reqBody4013.setAcctNo(bankAccountNo);
        reqBody4013.setCcyCode("RMB");
        reqBody4013.setBeginDate(DateTimeUtil.dateToString(startDate, "yyyyMMdd"));//yyyyMMdd
        reqBody4013.setEndDate(DateTimeUtil.dateToString(endDate, "yyyyMMdd"));//yyyyMMdd
        reqBody4013.setPageNo(String.valueOf(page));//1
        reqBody4013.setPageSize(String.valueOf(limit));//30
        //>>>>> 发送交易：获取交易结果
        ResResult<ReqBody4013, ResBody4013> result = postData(reqBody4013, ResBody4013.class);
        if (!result.getResHeader().getResCode().equals(PabXmlTools.RET_SUCCESS_CODE)) {
            throw new BusinessException(result.getResHeader().getResMsg());
        }
        return result.getResBody();
    }

    /**
     * 银行流水下载列表
     */
    @Override
    public ResBodyF00101 reqTradeDetailsDownloadList(Date queryDate, String bankAccountNo, String bsnCode) {
        ReqBodyF00101 reqBodyF00101 = new ReqBodyF00101();
        reqBodyF00101.setQueryDate(DateTimeUtil.dateToString(queryDate, "yyyyMMdd"));
        reqBodyF00101.setAccount(bankAccountNo);
        reqBodyF00101.setBsnCode(bsnCode);//交易类型
        //>>>>> 发送交易：获取交易结果
        ResResult<ReqBodyF00101, ResBodyF00101> result = postData(reqBodyF00101, ResBodyF00101.class);
        if (!result.getResHeader().getResCode().equals(PabXmlTools.RET_SUCCESS_CODE)) {
            throw new BusinessException(result.getResHeader().getResMsg());
        }
        return result.getResBody();
    }
    /**
     *  支付退票查询[4019]
     */
    @Override
    public ResBody4019 reqPaymentRefundQuery(String accNo, Date startDate, Date endDate, Integer page, Integer limit) {
        if (page == null || page < 0) page = 1;
        if (limit == null || limit < 1) limit =300;
        ReqBody4019 reqBody4019 = new ReqBody4019();
        reqBody4019.setAccNo(accNo);
        reqBody4019.setStartDate(DateTimeUtil.dateToString(startDate, "yyyyMMdd"));
        reqBody4019.setEndDate(DateTimeUtil.dateToString(endDate, "yyyyMMdd"));
        reqBody4019.setPageNo(String.valueOf(page));
        reqBody4019.setPageCts(String.valueOf(limit));
        //>>>>> 发送交易：获取交易结果
        ResResult<ReqBody4019, ResBody4019> result = postData(reqBody4019, ResBody4019.class);
        if (!result.getResHeader().getResCode().equals(PabXmlTools.RET_SUCCESS_CODE)) {
            throw new BusinessException(result.getResHeader().getResMsg());
        }
        return result.getResBody();
    }
    /**
     *  支付退票查询[4019] 定时函数
     */
    @Override
    public void taskReqPaymentRefundQuery(Integer page) {
        Date date=DateTimeUtil.plusDay(new Date(),-1);
        ResBody4019 resBody4019=reqPaymentRefundQuery(null,date ,date,page,300);
        String[] tradeNoList=resBody4019.getItemList().stream().map(ResBody4019.HOResultSet4019R::getsThirdVoucher).toArray(String[]::new);
        List<PabTradeRecord> pabTradeRecords= getTradeRecordByNoList(tradeNoList);
        pabTradeRecords.forEach(x->{
            ResBody4019.HOResultSet4019R hoResultSet4019R=resBody4019.getItemList().stream().filter(o -> Objects.equals(o.getsThirdVoucher(), x.getTradeNo())).findFirst().orElse(null);
            if(hoResultSet4019R!=null){
                x.setPabTradeStatus(PabTradeRecord.PabTradeStatus.fail);
                x.setBankMsg(hoResultSet4019R.getRejectRemark());
                x.setCompleteTime(DateTimeUtil.stringToDate(hoResultSet4019R.getRejectDate(),"yyyy-MM-dd HH:mm:ss"));
                x.setNotifiedYilin(false);//防止异常通知失败，不继续通知
                x.setNotifyTime(null);
            }
        });
        try {
            notifiedYilin(pabTradeRecords);
        } finally {
            if(Objects.equals(resBody4019.getiEnd(), "N"))taskReqPaymentRefundQuery(Integer.parseInt(resBody4019.getPageNo())+1);
        }

    }
    /**
     * 电子回单列表
     */
    @Override
    public ResBodyELC009 reqTradeReceiptList(String outAccNo, Date beginDate, Date endDate, String inAccNo, Integer page, Integer limit) {
        ReqBodyELC009 reqBodyELC009 = new ReqBodyELC009();
        reqBodyELC009.setOutAccNo(outAccNo);
        reqBodyELC009.setAccountBeginDate(DateTimeUtil.dateToString(beginDate, "yyyyMMdd"));
        reqBodyELC009.setAccountEndDate(DateTimeUtil.dateToString(endDate, "yyyyMMdd"));//
        reqBodyELC009.setInAccNo(inAccNo);
        reqBodyELC009.setRecordStartNo(String.valueOf(page));//
        reqBodyELC009.setRecordNumber(String.valueOf(limit));//
        //>>>>> 发送交易：获取交易结果
        ResResult<ReqBodyELC009, ResBodyELC009> result = postData(reqBodyELC009, ResBodyELC009.class);
        if (!result.getResHeader().getResCode().equals(PabXmlTools.RET_SUCCESS_CODE)) {
            throw new BusinessException(result.getResHeader().getResMsg());
        }
        return result.getResBody();
    }

    /**
     * 创建电子回单PDF下载文件 ELC010
     */
    @Override
    public ResBodyELC010 createReceiptDownloadPDF(String outAccNo, Date beginDate, Date endDate, String pdfNum, List<ReqBodyELC010.ReqBodyELC010Item> list) {
        ReqBodyELC010 reqBodyELC010 = new ReqBodyELC010();
        reqBodyELC010.setOutAccNo(outAccNo);
        reqBodyELC010.setAccountBeginDate(DateTimeUtil.dateToString(beginDate, "yyyyMMdd"));
        reqBodyELC010.setAccountEndDate(DateTimeUtil.dateToString(endDate, "yyyyMMdd"));//
        reqBodyELC010.setPdfNum(pdfNum);
        reqBodyELC010.setItemList(list);//三个数据必输
        //>>>>> 发送交易：获取交易结果
        ResResult<ReqBodyELC010, ResBodyELC010> result = postData(reqBodyELC010, ResBodyELC010.class);
        if (!result.getResHeader().getResCode().equals(PabXmlTools.RET_SUCCESS_CODE)) {
            throw new BusinessException(result.getResHeader().getResMsg());
        }
        //创建成功直接请求下载
        if (result.getResBody() == null) throw new BusinessException("返回数据为空");
        reqDownloadTradeDetails(result.getResBody().getFileName(), result.getResBody().getRandomPwd());
        return result.getResBody();
    }

    /**
     * 银行对账列表
     */
    @Override
    public ResBodyEDZ001 getTradeReconPage(String outAccNo, Date queryDate, String checkFlag, Integer page, Integer limit) {
        ReqBodyEDZ001 reqBodyEDZ001 = new ReqBodyEDZ001();
        reqBodyEDZ001.setAcctNo(outAccNo);
        reqBodyEDZ001.setQueryDate(DateTimeUtil.dateToString(queryDate, "yyyyMMdd"));
        reqBodyEDZ001.setPageNo(String.valueOf(page));
        reqBodyEDZ001.setPageSize(String.valueOf(limit));//
        reqBodyEDZ001.setCheckFlag(checkFlag);
        //>>>>> 发送交易：获取交易结果
        ResResult<ReqBodyEDZ001, ResBodyEDZ001> result = postData(reqBodyEDZ001, ResBodyEDZ001.class);
        if (!result.getResHeader().getResCode().equals(PabXmlTools.RET_SUCCESS_CODE)) {
            throw new BusinessException(result.getResHeader().getResMsg());
        }
        return result.getResBody();
    }

    /**
     * 银行对账同步
     */
    @Override
    public ResBodyEDZ002 syncTradeResult(String acctNo, String balanceDate, List<ReqBodyEDZ002.ReqBodyEDZ002Item> itemList) {
        ReqBodyEDZ002 reqBodyEDZ002 = new ReqBodyEDZ002();
        reqBodyEDZ002.setAcctNo(acctNo);
        reqBodyEDZ002.setBalanceDate(balanceDate);
        reqBodyEDZ002.setUpdateCts(String.valueOf(itemList.size()));
        reqBodyEDZ002.setItemList(itemList);
        //>>>>> 发送交易：获取交易结果
        ResResult<ReqBodyEDZ002, ResBodyEDZ002> result = postData(reqBodyEDZ002, ResBodyEDZ002.class);
        if (!result.getResHeader().getResCode().equals(PabXmlTools.RET_SUCCESS_CODE)) {
            throw new BusinessException(result.getResHeader().getResMsg());
        }
        return result.getResBody();
    }

    /**
     * 公共请求下载
     */
    @Override
    public ResBodyFILE03 reqDownloadTradeDetails(String fileName, String randomPwd) {
        PabFileDownLoad fileDownLoad = new PabFileDownLoad();
        fileDownLoad.setFileName(fileName);
        fileDownLoad.setTradeSn(DateTimeUtil.dateNo("SN"));
        ReqBodyFILE03 reqBodyFILE03 = new ReqBodyFILE03();
        reqBodyFILE03.setTradeSn(fileDownLoad.getTradeSn());
        reqBodyFILE03.setFileName(fileName);
        reqBodyFILE03.setRandomPwd(randomPwd);
        fileDownLoad.setRandomPwd(randomPwd);
        fileDownLoad.setFileDesc("正在下载");
        pabFileDownloadMapper.insert(fileDownLoad);
        //>>>>> 发送交易：获取交易结果
        ResResult<ReqBodyFILE03, ResBodyFILE03> result = postData(reqBodyFILE03, ResBodyFILE03.class);
        if (!result.getResHeader().getResCode().equals(PabXmlTools.RET_SUCCESS_CODE)) {
            throw new BusinessException(result.getResHeader().getResMsg());
        }
        return result.getResBody();
    }

    /**
     * 通知 处理区
     */
    @Override
    public String dealPabCallReq(String pabReqXml) {
        String resBodyXml = null; //最终返回的BODY-XML//
        ResResult<BaseResult, BaseResult> resResult = new ResResult<>();
        DataXmlHeader reqHeader = pabXmlTools.dealNoticeXmlHeader(pabReqXml);
        resResult.setReqHeader(reqHeader);
        //一共需要处理4个通知 分别解析返回对应的实体类
        if ("4005NT".equals(reqHeader.getTradeCode())) {//单笔
            ReqBody4005NT reqBody4005NT = pabXmlTools.dealNoticeXmlBody(pabReqXml, ReqBody4005NT.class);
            resResult.setReqBody(reqBody4005NT);
            deal4005NT(reqBody4005NT);
            resBodyXml = pabXmlTools.buildXmlBody(new ResBody4005NT());
        } else if ("4013NT".equals(reqHeader.getTradeCode())) {//账务变动通知
            ReqBody4013NT reqBody4013NT = pabXmlTools.dealNoticeXmlBody(pabReqXml, ReqBody4013NT.class);
            resResult.setReqBody(reqBody4013NT);
            deal4013NT(reqBody4013NT);
            resBodyXml = pabXmlTools.buildXmlBody(new ResBody4005NT());
        } else if ("KFNT01".equals(reqHeader.getTradeCode())) {
            //throw new BusinessException("缺省,跨行快付通知");
            resBodyXml = "";
        } else if ("FILE04".equals(reqHeader.getTradeCode())) {
            ReqBodyFILE04 reqBodyFILE04 = pabXmlTools.dealNoticeXmlBody(pabReqXml, ReqBodyFILE04.class);
            resResult.setReqBody(reqBodyFILE04);
            dealFILE04(reqBodyFILE04);
            resBodyXml = pabXmlTools.buildXmlBody(new ResBody4005NT());
        }
        int resBodyXmlLength = pabXmlTools.getDataXmlLength(resBodyXml);
        DataXmlHeader resHeader = pabXmlTools.buildRetHeader(reqHeader, resBodyXmlLength);
        resResult.setResHeader(resHeader);
        saveDockingLog(resResult);
        return resHeader.toString() + resBodyXml;
    }

    //通知接收:单笔转账结果通知[4005NT]
    private void deal4005NT(ReqBody4005NT reqBody4005NT) {
        PabTradeRecord tradeRecord = getTradeRecordByNo(reqBody4005NT.getOrigThirdVoucher());
        if (tradeRecord == null) return;
        //标记过一次状态
        putUpdatePaymentStatus(tradeRecord, reqBody4005NT.getStt());
        tradeRecord.setBankFlowNo(reqBody4005NT.getFrontLogNo());
        tradeRecord.setCompleteTime(DateTimeUtil.stringToDate(reqBody4005NT.getSubmitTime(), "yyyyMMddHHmmss"));
        //交易退票
        if (reqBody4005NT.getIsBack().equals("1")){
            tradeRecord.setPabTradeStatus(PabTradeRecord.PabTradeStatus.fail);
        }
        tradeRecord.setFailMsg(reqBody4005NT.getBackRem());
        pabTradeRecordMapper.updateById(tradeRecord);
        saveTradeFlow("基本转账交易", tradeRecord);
        //通知主服务器
        notifiedYilin(ArrayUtil.initList(tradeRecord));
    }

    //通知接收:账务变动通知[4013NT]
    private void deal4013NT(ReqBody4013NT reqBody4013NT) {
        PabTradeRecord tradeRecord = new PabTradeRecord();
        //从附言栏查找交易关键字段 TD169115436227029353
        String purpose = reqBody4013NT.getPurpose();
        if (purpose.contains("TD")) {
            int beginIndex = purpose.indexOf("TD");
            String tradeNo = purpose.substring(beginIndex, beginIndex + 20);
            tradeRecord.setTradeNo(tradeNo);
        }
        tradeRecord.setPabTradeType(PabTradeRecord.PabTradeType.pubCollection);
        tradeRecord.setOutAcctBankNode(reqBody4013NT.getOutBankNo());//付款人开户行行号
        tradeRecord.setOutAcctBankName(reqBody4013NT.getOutBankName());//付款人开户行名称
        tradeRecord.setOutAcctNo(reqBody4013NT.getOutAcctNo());//付款人账户*
        tradeRecord.setOutAcctName(reqBody4013NT.getOutAcctName());//付款人名称*
        tradeRecord.setInAcctBankNode(reqBody4013NT.getInBankNo());//收款人开户行行号
        tradeRecord.setInAcctBankName(reqBody4013NT.getInBankName());//收款人开户行名称
        tradeRecord.setInAcctNo(reqBody4013NT.getInAcctNo());//收款人账户*
        tradeRecord.setInAcctName(reqBody4013NT.getInAcctName());//收款人名称*
        tradeRecord.setTranAmount(Double.parseDouble(reqBody4013NT.getTranAmount()));//交易金额
        tradeRecord.setCompleteTime(DateTimeUtil.stringToDate(reqBody4013NT.getTxTime(), "yyyyMMddHHmmss"));
        tradeRecord.setBankFlowNo(reqBody4013NT.getVoucherNo());
        tradeRecord.setPabTradeStatus(PabTradeRecord.PabTradeStatus.success);
        pabTradeRecordMapper.insert(tradeRecord);
        saveTradeFlow("自动收款同步", tradeRecord);
        //通知主服务器
        notifiedYilin(ArrayUtil.initList(tradeRecord));
    }

    //通知接收: 文件结果通知[FILE04]
    private void dealFILE04(ReqBodyFILE04 reqBodyFILE04) {
        PabFileDownLoad fileDownLoad = getFileDownLoadBySn(reqBodyFILE04.getTradeSn());
        if (fileDownLoad==null) fileDownLoad = new PabFileDownLoad();
        fileDownLoad.setFileName(reqBodyFILE04.getFileName());
        fileDownLoad.setFileSize(reqBodyFILE04.getFileSize());
        fileDownLoad.setFileDesc(reqBodyFILE04.getDesc());
        fileDownLoad.setCode(reqBodyFILE04.getCode());
        fileDownLoad.setFilePath(reqBodyFILE04.getFilePath());
        String downloadPath = generateFileDownload(reqBodyFILE04.getFileName());
        fileDownLoad.setDownloadUrl(downloadPath);
        //通过文件路径生成下载地址，待定
        pabFileDownloadMapper.updateById(fileDownLoad);
    }

    //公共: 通知易林方主服务器
    private void notifiedYilin(List<PabTradeRecord> tradeRecords) {
        Map<String, Object> generate = MapUtil.generate(map -> {
            List<Map<String,Object>> tradeResults = ArrayUtil.fieldAdd(tradeRecords, obj -> {
                String stateName = "create";
                if (obj.getPabTradeStatus() == PabTradeRecord.PabTradeStatus.success) stateName = "paySuccess";
                if (obj.getPabTradeStatus() == PabTradeRecord.PabTradeStatus.fail) stateName = "payFail";
                String finalStateName = stateName;
                return MapUtil.generate(map1 -> {
                    map1.put("tradeNo",obj.getTradeNo());
                    map1.put("tradeStatus", finalStateName);
                    map1.put("bankFlowNo",obj.getBankFlowNo());
                    map1.put("tradeFailMsg",obj.getFailMsg());
                    map1.put("finishTime",obj.getCompleteTime());
                });
            });
            map.put("tradeResults", JsonUtil.objectToJson(tradeResults));
            map.put("payType", "bankPay");
            map.put("bankType", "pabBankPay");
        });
        String success = userService.request("/capital/callback/bankPayResult", generate, new TypeToken<String>() {});
        if (success.contains("SUCCESS")) {
            for (PabTradeRecord tradeRecord : tradeRecords) {
                tradeRecord.setNotifiedYilin(true);//是否已通知主服务器
                tradeRecord.setNotifyTime(new Date());//通知时间
                pabTradeRecordMapper.updateById(tradeRecord);
            }
        }
    }

    //公共: 设置状态//处理交易结果
    private void putUpdatePaymentStatus(PabTradeRecord tradeRecord, String stt) {
        tradeRecord.setPabTradeType(PabTradeRecord.PabTradeType.platPayment);
        boolean success = stt.equals("20"), fail = stt.equals("30");
        if (success || fail) {
            tradeRecord.setCompleteTime(new Date());
            tradeRecord.setPabTradeStatus(success ? PabTradeRecord.PabTradeStatus.success : PabTradeRecord.PabTradeStatus.fail);
        } else {
            tradeRecord.setPabTradeStatus(PabTradeRecord.PabTradeStatus.handle);
        }
    }

    //公共：转发请求并保存日志
    public <R, S> ResResult<R, S> postData(R reqBody, Class<S> cls) {
        ResResult<R, S> reqResResResult = pabXmlTools.postData(reqBody, cls);
        saveDockingLog(reqResResResult);
        return reqResResResult;
    }

    //公共: 保存日志
    private void saveDockingLog(ResResult result) {
        PabTrade trade = result.getReqBody().getClass().getAnnotation(PabTrade.class);
        String reqDate = String.valueOf(result.getReqHeader().getReqDate());
        String reqTime = String.valueOf(result.getReqHeader().getReqTime());
        PabDockingLog dockingLog = new PabDockingLog();
        dockingLog.setTitle(trade.value() + ": " + trade.title());
        dockingLog.setReqTime(DateTimeUtil.stringToDate(reqDate + reqTime, "yyyyMMddHHmmss"));
        dockingLog.setReqXml(result.getReqHeader().toString() + pabXmlTools.buildXmlBody(result.getReqBody()));
        dockingLog.setResXml(result.getResHeader().toString() + pabXmlTools.buildXmlBody(result.getResBody()));
        dockingLog.setDockingPass(result.getResHeader().getResCode().equals("000000"));
        pabDockingLogMapper.insert(dockingLog);
    }

    //公共: 保存流水
    private void saveTradeFlow(String tradeName, PabTradeRecord tradeRecord) {
        if (tradeRecord.getPabTradeStatus() != PabTradeRecord.PabTradeStatus.success) return;
        TradeFlow[] tradeFlows = tradeFlowService.generateTradeFlows(tradeName, TradeFlow.PayType.pabPay, tradeRecord);
        tradeFlowService.saveOrUpdateBatch(ArrayUtil.initList(tradeFlows));
    }

    // - 本地

    /**
     * 获取下载任务列表
     */
    @Override
    public PageData<PabFileDownLoad> getPabFileDownLoadList(Integer page, Integer limit) {
        QueryWrapper<PabFileDownLoad> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("update_time");
        return pabFileDownloadMapper.selectPage(new PageData<>(page, limit), queryWrapper);
    }
    /**
     * 根据批次NO获取交易记录
     */
    @Override
    public List<PabTradeRecord> getTradeRecordsByBatchNo(String batchNo) {
        QueryWrapper<PabTradeRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("batch_no", batchNo);
        return pabTradeRecordMapper.selectList(queryWrapper);
    }

    /**
     * 获取本地交易记录列表
     */
    @Override
    public PageData<PabTradeRecord> getTradeRecordPage(
            PabTradeRecord.PabTradeStatus pabTradeStatus,
            String inAcctName,
            Integer page, Integer limit) {
        QueryWrapper<PabTradeRecord> queryWrapper = new QueryWrapper<>();
        if (pabTradeStatus != null) queryWrapper.eq("pab_trade_status", pabTradeStatus.index);
        if (inAcctName!=null)queryWrapper.like("in_acct_name", inAcctName);
        return pabTradeRecordMapper.selectPage(new PageData<>(page, limit), queryWrapper);
    }

    /**
     * 根据单号NO获取交易记录
     */
    @Override
    public PabTradeRecord getTradeRecordByNo(String tradeNo) {
        QueryWrapper<PabTradeRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("trade_no", tradeNo);
        return pabTradeRecordMapper.selectOne(queryWrapper);
    }
    @Override
    public List<PabTradeRecord> getTradeRecordByNoList(String[] tradeNos) {
        QueryWrapper<PabTradeRecord> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("trade_no", Arrays.asList(tradeNos));
        return pabTradeRecordMapper.selectList(queryWrapper);
    }


    /**
     * 根据单号NO获取下载记录
     */
    @Override
    public PabFileDownLoad getFileDownLoadBySn(String tradeSn) {
        QueryWrapper<PabFileDownLoad> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("trade_sn", tradeSn);
        return pabFileDownloadMapper.selectOne(queryWrapper);
    }

    /**
     * 获取操作日志
     */
    @Override
    public PageData<PabDockingLog> getDockingLogPage(Integer page, Integer limit) {
        QueryWrapper<PabDockingLog> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("update_time");
        return pabDockingLogMapper.selectPage(new PageData<>(page, limit), queryWrapper);
    }

    /**
     * 生成文件下载路径
     */
    @Override
    public String generateFileDownload(String fileName) {
        //前置分离时，先走FTP下载,默认保存在Pab文件夹 /Pab -> D:\XbrFiles\Pab\  ..+ fileName
        if (separation) fptClient.ftpDownload(downloadPath, savePath + downloadPath, fileName);
        //检查savePath里面是否存在文件
        if (!new File(savePath + downloadPath + fileName).exists()) return null;
        //返回映射路径即可
        return AppUtil.getLocalPath() + mappingPath + "/" + downloadPath + fileName;
    }

    /**
     * 系统探测
     */
    public boolean bankNetStatus() {
        ReqBodyS001 reqBodyS001 = new ReqBodyS001();
        ResResult<ReqBodyS001, ResBodyS001> result = pabXmlTools.postData(reqBodyS001, ResBodyS001.class);
        return result.getResHeader().getResCode().equals(PabXmlTools.RET_SUCCESS_CODE);
    }

    @Override
    public Map<String, String> getServerStatusBag() {
        Map<String, String> map = new HashMap<>();
        try {
            InetAddress address = InetAddress.getByName(AppUtil.getIpAddressByUrl(reqUrl));
            boolean frontNetStatus = address.isReachable(1000);
            boolean bankNetStatus = bankNetStatus();
            if (!frontNetStatus) map.put("netStatus", "frontErr");
            else if (!bankNetStatus) map.put("netStatus", "bankErr");
            else map.put("netStatus", "normal");
            map.put("separation", separation ? "是" : "否");
            map.put("merchantCode", merchantCode);
            map.put("download", savePath + downloadPath);
            map.put("reqUrl", reqUrl);
            long ii = System.currentTimeMillis() - startupTimeListener.getStartupTime();
            map.put("distanceTime", DateTimeUtil.getDistanceTime(ii));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return map;
    }

}
